package p.ithorns.framework.feign.enhance;

import feign.Request;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.openfeign.OptionsFactoryBean;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;

import javax.annotation.Nonnull;

/**
 * FeignServiceScanner
 *
 * @author Ht.L
 * @date 2024-03-28 15:39
 * @since 1.0.0
 */
public class FeignSvcScanner extends ClassPathBeanDefinitionScanner {

    private final static Logger log = LoggerFactory.getLogger(FeignSvcScanner.class);

    private final BeanFactory beanFactory;

    public FeignSvcScanner(BeanDefinitionRegistry registry, BeanFactory factory) {
        super(registry, false);
        this.beanFactory = factory;
    }

    @Override
    public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return metadata.isIndependent() && !metadata.isAnnotation();
    }


    @SuppressWarnings("rawtypes")
    @Override
    protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder,
                                          @Nonnull  BeanDefinitionRegistry registry) {
        AnnotatedBeanDefinition abd = (AnnotatedBeanDefinition) definitionHolder.getBeanDefinition();
        AnnotationMetadata metadata = abd.getMetadata();
        Assert.isTrue(metadata.isInterface(), "@FeignSvc can only be specified on an interface");

        String beanClassName = metadata.getClassName();
        try {
            final Class  interfaceClass = Class.forName(beanClassName);
            // 从spring中获取实例
            final Object impl = beanFactory.getBean(interfaceClass);
            String targetFeignName = StringUtils.capitalize(interfaceClass.getSimpleName()) + "$Feign";
            BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(interfaceClass, () -> {
                FeignSvcFactoryBean<?> factoryBean = new FeignSvcFactoryBean<>(interfaceClass, impl);
                return factoryBean.getObject();
            });

            AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            definition.setLazyInit(false);
            definition.setPrimary(true);

            beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, targetFeignName);
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, targetFeignName);
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }


    private void registerOptionsBeanDefinition(BeanDefinitionRegistry registry, String contextId) {
        String beanName = Request.Options.class.getCanonicalName() + "-" + contextId;
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
                .genericBeanDefinition(OptionsFactoryBean.class);
        definitionBuilder.setScope("refresh");
        definitionBuilder.addPropertyValue("contextId", contextId);
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definitionBuilder.getBeanDefinition(),
                beanName);
        definitionHolder = ScopedProxyUtils.createScopedProxy(definitionHolder, registry, true);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }

}